package com.xinQing.blogme.lucene;

import com.xinQing.blogme.entity.Article;
import lombok.extern.slf4j.Slf4j;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.TextField;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.index.Term;
import org.apache.lucene.queryparser.classic.MultiFieldQueryParser;
import org.apache.lucene.search.BooleanClause;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * Created by null on 2017/8/7.
 */
@Slf4j
@Component
public class LuceneServiceImpl implements LuceneService {

    @Value("${lucene.indexPath}")
    private String indexPath;

    @Override
    public void createIndex(Article article) {
        IndexWriter indexWriter = null;
        try {
            // 1、创建Directory
            /*
             * 注意open方法与3.5版本和4.5版本的不同：
             *
             * 这里不再接受一个File而是Path，使用的是JDK1.7的新特性，也就是说5.0版本是基于JDK1.7开发的
             *
             * 如何获取Path，请参照 Java7新特性--Path http://blog.csdn.net/zpf336/article/details/45074445
             *
             */
            Directory directory = FSDirectory.open(Paths.get(indexPath));
            // 2、创建IndexWriter
            /*
             * 注意StandardAnalyzer与3.5版本的不同：
             *
             * StandardAnalyzer不在lucene-core包中而在lucene-analyzers-common包中 从4.0版本以后分离
             *
             * 并且不需要提供版本号
             */
            Analyzer analyzer = new StandardAnalyzer();

            /*
             * 注意IndexWriterConfig与3.5版本和4.5版本的不同：
             *
             * 不需要提供版本号
             */
            IndexWriterConfig indexWriterConfig = new IndexWriterConfig(analyzer);
            indexWriter = new IndexWriter(directory, indexWriterConfig);

            // 3、创建Document对象
            Document document = new Document();
            // 4、为Document添加Field
            /*
             * 注意Field与3.5版本的不同：两个参数的构造器已过时，使用如下构造器
             *
             * 但是和4.5版本是相同的
             */
            // 第三个参数是FieldType 但是定义在TextField中作为静态变量，看API也不好知道怎么写
            document.add(new Field("id", article.getId(), TextField.TYPE_STORED));
            document.add(new Field("name", article.getName(), TextField.TYPE_STORED));
            document.add(new Field("content", article.getContent(), TextField.TYPE_NOT_STORED));
            document.add(new Field("description", article.getDescription(), TextField.TYPE_STORED));
            document.add(new Field("createTime", article.getCreateTime().getTime() + "", TextField.TYPE_STORED));
            document.add(new Field("updateTime", article.getUpdateTime().getTime() + "", TextField.TYPE_STORED));

            // 5、通过IndexWriter添加文档到索引中
            indexWriter.addDocument(document);

            indexWriter.commit();
        } catch (Exception e) {
            log.warn("{}", e);
        } finally {
            try {
                if (indexWriter != null) {
                    indexWriter.close();
                }
            } catch (Exception e) {
                log.warn("{}", e);
            }
        }
    }

    @Override
    public List<Article> search(String word) {
        DirectoryReader directoryReader = null;
        List<Article> articles = new ArrayList<>();
        try {
            // 1、创建Directory
            Directory directory = FSDirectory.open(Paths.get(indexPath));

            // 2、创建IndexReader
                /*
                 * 注意Reader与3.5版本不同：
                 *
                 * 所以使用DirectoryReader
                 *
                 * @Deprecated public static DirectoryReader open(final Directory directory) throws IOException { return
                 *             DirectoryReader.open(directory); }
                 *
                 *             但是和4.5版本相同
                 */
            // 如下方法过时
            // IndexReader indexReader = IndexReader.open(directory);
            directoryReader = DirectoryReader.open(directory);

            // 3、根据IndexReader创建IndexSearch
            IndexSearcher indexSearcher = new IndexSearcher(directoryReader);

            // 4、创建搜索的Query
                /*
                 * 注意StandardAnalyzer与3.5版本4.5版本不同：
                 *
                 * 不需要版本号
                 */
            Analyzer analyzer = new StandardAnalyzer();
            // 创建parser来确定要搜索文件的内容，第一个参数为搜索的域
            /*
             * 注意QueryParser与3.5版本4.5版本不同：
             *
             * 不需要版本号
             */
            // 多个关键字一起搜索
            String[] queries = {word, word, word};
            String[] fields = {"name", "content",  "description"};
            BooleanClause.Occur[] clauses = {BooleanClause.Occur.SHOULD, BooleanClause.Occur.SHOULD, BooleanClause.Occur.SHOULD};
            Query query = MultiFieldQueryParser.parse(queries, fields, clauses, analyzer);

            // 5、根据searcher搜索并且返回TopDocs
            TopDocs topDocs = indexSearcher.search(query, 10);

            // 6、根据TopDocs获取ScoreDoc对象
            ScoreDoc[] scoreDocs = topDocs.scoreDocs;
            for (ScoreDoc scoreDoc : scoreDocs) {
                // 7、根据searcher和ScoreDoc对象获取具体的Document对象
                Document document = indexSearcher.doc(scoreDoc.doc);

                // 8、根据Document对象获取需要的值
                Article article = new Article();
                article.setId(document.get("id"));
                article.setName(document.get("name"));
                article.setDescription(document.get("description"));
                article.setCreateTime(new Date(Long.parseLong(document.get("createTime"))));
                article.setUpdateTime(new Date(Long.parseLong(document.get("updateTime"))));
                articles.add(article);
            }
        } catch (Exception e) {
            log.warn("{}", e);
        } finally {
            try {
                if (directoryReader != null) {
                    directoryReader.close();
                }
            } catch (Exception e) {
                log.warn("{}", e);
            }
        }
        return articles;
    }

    /**
     * 删除文章索引
     *
     * @param id
     */
    @Override
    public void deleteIndex(String id) {
        IndexWriter indexWriter = null;
        try {
            Directory directory = FSDirectory.open(Paths.get(indexPath));
            Analyzer analyzer = new StandardAnalyzer();
            IndexWriterConfig indexWriterConfig = new IndexWriterConfig(analyzer);
            indexWriter = new IndexWriter(directory, indexWriterConfig);
            indexWriter.deleteDocuments(new Term("id", id));
            indexWriter.commit();
        } catch (IOException e) {
            log.warn("{}", e);
        } finally {
            try {
                if (indexWriter != null) {
                    indexWriter.close();
                }
            } catch (Exception e) {
                log.warn("{}", e);
            }
        }
    }

    @Override
    public void updateIndex(Article article) {
        IndexWriter indexWriter = null;
        try {
            Directory directory = FSDirectory.open(Paths.get(indexPath));
            Analyzer analyzer = new StandardAnalyzer();
            IndexWriterConfig indexWriterConfig = new IndexWriterConfig(analyzer);
            indexWriter = new IndexWriter(directory, indexWriterConfig);

            Document document = new Document();
            document.add(new Field("id", article.getId(), TextField.TYPE_STORED));
            document.add(new Field("name", article.getName(), TextField.TYPE_STORED));
            document.add(new Field("content", article.getContent(), TextField.TYPE_NOT_STORED));
            document.add(new Field("description", article.getDescription(), TextField.TYPE_STORED));
            article.setCreateTime(new Date(Long.parseLong(document.get("createTime"))));
            article.setUpdateTime(new Date(Long.parseLong(document.get("updateTime"))));

            indexWriter.updateDocument(new Term("id", article.getId()), document);
            indexWriter.commit();
        } catch (IOException e) {
            log.warn("{}", e);
        } finally {
            try {
                if (indexWriter != null) {
                    indexWriter.close();
                }
            } catch (Exception e) {
                log.warn("{}", e);
            }
        }
    }
}
